#ifndef _BUOLA_MEMORY_UMEMORYPOOL_H_
#define _BUOLA_MEMORY_UMEMORYPOOL_H_

#include <buola/buola.h>

namespace buola {

///////////////////////////////////////////////////////////////////////
// Memory pool

//! This class is used by the parser to create new nodes and attributes, without overheads of dynamic memory allocation.
//! In most cases, you will not need to use this class directly. 
//! However, if you need to create nodes manually or modify names/values of nodes, 
//! you are encouraged to use memory_pool of relevant xml_document to allocate the memory. 
//! Not only is this faster than allocating them by using <code>new</code> operator, 
//! but also their lifetime will be tied to the lifetime of document, 
//! possibly simplyfing memory management. 
//! <br><br>
//! Call allocate_node() or allocate_attribute() functions to obtain new nodes or attributes from the pool. 
//! You can also call allocate_string() function to allocate strings.
//! Such strings can then be used as names or values of nodes without worrying about their lifetime.
//! Note that there is no <code>free()</code> function -- all allocations are freed at once when clear() function is called, 
//! or when the pool is destroyed.
//! <br><br>
//! It is also possible to create a standalone memory_pool, and use it 
//! to allocate nodes, whose lifetime will not be tied to any document.
//! <br><br>
//! Pool maintains <code>RAPIDXML_STATIC_POOL_SIZE</code> bytes of statically allocated memory. 
//! Until static memory is exhausted, no dynamic memory allocations are done.
//! When static memory is exhausted, pool allocates additional blocks of memory of size <code>RAPIDXML_DYNAMIC_POOL_SIZE</code> each,
//! by using global <code>new[]</code> and <code>delete[]</code> operators. 
//! This behaviour can be changed by setting custom allocation routines. 
//! Use set_allocator() function to set them.
//! <br><br>
//! Allocations for nodes, attributes and strings are aligned at <code>RAPIDXML_ALIGNMENT</code> bytes.
//! This value defaults to the size of pointer on target architecture.
//! <br><br>
//! To obtain absolutely top performance from the parser,
//! it is important that all nodes are allocated from a single, contiguous block of memory.
//! Otherwise, cache misses when jumping between two (or more) disjoint blocks of memory can slow down parsing quite considerably.
//! If required, you can tweak <code>RAPIDXML_STATIC_POOL_SIZE</code>, <code>RAPIDXML_DYNAMIC_POOL_SIZE</code> and <code>RAPIDXML_ALIGNMENT</code> 
//! to obtain best wasted memory to performance compromise.
//! To do it, define their values before rapidxml.hpp file is included.
//! \param Ch Character type of created nodes. 
template<size_t vBlockSize=65536,size_t vAlignment=sizeof(void*)>
class UMemoryPool
{
    struct SHeader
    {
        char *mPrevious;
    };
    
public:
    ///constructs empty pool with default allocator functions.
    UMemoryPool()
    {
        Init();
    }

    //! Destroys pool and frees all the memory. 
    //! This causes memory occupied by nodes allocated by the pool to be freed.
    //! Nodes allocated from the pool are no longer valid.
    ~UMemoryPool()
    {
        Clear();
    }
    
    template<typename tType,typename... tArgs>
    void construct(tType *pObject,tArgs&&... pArgs)
    {
        new((void*)pObject) tType(std::forward<tArgs>(pArgs)...);
    }

    void* allocate(size_t pSize)
    {
        // If not enough memory left in current pool, allocate a new pool
        if(mPtr+pSize>mEnd)
        {
            // Calculate required pool size (may be bigger than RAPIDXML_DYNAMIC_POOL_SIZE)
            std::size_t lPoolSize=std::max(pSize+sizeof(SHeader)+vAlignment,vBlockSize);
            
            // Allocate
            char *lRaw=alloc_array<char>(lPoolSize,vAlignment);
                
            // Setup new pool in allocated memory
            SHeader *lHeader=reinterpret_cast<SHeader*>(lRaw);
            lHeader->mPrevious=mBegin;
            mBegin=lRaw;
            mPtr=get_next_aligned_pos(lRaw+sizeof(SHeader));
            mEnd=lRaw+lPoolSize;
        }

        // Update pool and return aligned pointer
        void *lResult=mPtr;
        mPtr=get_next_aligned_pos(mPtr+pSize,vAlignment);
        return lResult;
        
    }
    
    //! Clears the pool. 
    //! This causes memory occupied by nodes allocated by the pool to be freed.
    //! Any nodes or strings allocated from the pool will no longer be valid.
    void Clear()
    {
        while (mBegin!=mStaticMemory)
        {
            char *lPrevious=reinterpret_cast<SHeader*>(mBegin)->mPrevious;
            free(mBegin);
            mBegin=lPrevious;
        }
        Init();
    }
    
private:
    void Init()
    {
        mBegin=mStaticMemory;
        mPtr=get_next_aligned_pos(mBegin);
        mEnd=mStaticMemory+vBlockSize;
    }
    
    char *mBegin;                                      // Start of raw memory making up current pool
    char *mPtr;                                        // First algined free byte in current pool (always aligned!)
    char *mEnd;                                        // One past last available byte in current pool
    char mStaticMemory[vBlockSize];    // Static raw memory
};
    
/*namespace buola*/ } 

template<size_t vBlockSize,size_t vAlignment>
void *operator new(size_t pSize,buola::UMemoryPool<vBlockSize,vAlignment> &pPool)  {   return pPool.allocate(pSize);   }

#endif
